package by.pavlyukevich.paymentsystem.dataaccess.impl;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Fetch;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.stereotype.Repository;

import by.pavlyukevich.paymentsystem.dataaccess.AccountDao;
import by.pavlyukevich.paymentsystem.dataaccess.filter.AccountFilter;
import by.pavlyukevich.paymentsystem.dataaccess.filter.Filter;
import by.pavlyukevich.paymentsystem.datamodel.Account;
import by.pavlyukevich.paymentsystem.datamodel.Account_;
import by.pavlyukevich.paymentsystem.datamodel.user.UserAccount;
import by.pavlyukevich.paymentsystem.datamodel.user.UserAccount_;

@Repository
public class AccountDaoImpl extends AbstractDaoImpl<Integer, Account> implements AccountDao {

	protected AccountDaoImpl() {
		super(Account.class);
	}

	@Override
	public List<Account> getList(Filter<Account> accountFilter) {

		CriteriaQuery<Account> criteria = getAllCriteria(accountFilter);

		addCriteriaRestrict(criteria, accountFilter);

		Root<Account> root = getRoot(criteria, Account.class);
		Fetch<Account, UserAccount> userFetch = root.fetch(Account_.userAccount);
		userFetch.fetch(UserAccount_.individual, JoinType.LEFT);
		userFetch.fetch(UserAccount_.contractor, JoinType.LEFT);

		return getList(criteria, accountFilter);
	}

	@Override
	public long getCount(Filter<Account> accountFilter) {
		CriteriaQuery<Long> criteria = getCountAllCriteria();
		addCriteriaRestrict(criteria, accountFilter);
		return getSingle(criteria);
	}

	private <T> CriteriaQuery<T> addCriteriaRestrict(CriteriaQuery<T> criteria,
			Filter<Account> filter) {
		if (filter == null) {
			return criteria;
		}
		Root<Account> root = getRoot(criteria, Account.class);
		CriteriaBuilder cBuilder = getEntityManager().getCriteriaBuilder();
		List<Predicate> predicateList = new ArrayList<Predicate>();
		AccountFilter accountFilter = (AccountFilter) filter;
		if (accountFilter.getUserAccount() != null) {
			predicateList.add(cBuilder.equal(root.get(Account_.userAccount),
					accountFilter.getUserAccount()));
		}
		if (accountFilter.getAccount() != null) {
			predicateList.add(cBuilder.equal(root.get(Account_.id), accountFilter.getAccount()
					.getId()));
		}

		if (predicateList.size() > 0) {
			Predicate predicateArray[] = predicateList.toArray(new Predicate[0]);
			criteria.where(predicateArray);
		}

		return criteria;
	}
}
